import axios, { type AxiosInstance, type AxiosRequestConfig } from "axios"
import { useUserStoreHook } from "@/store/modules/user"
import { ElMessage } from "element-plus"
import { get, merge } from "lodash-es"
import { getToken } from "./cache/cookies"
import router from "@/router"

/** 退出登录并强制刷新页面（会重定向到登录页） */
function logout() {
  useUserStoreHook().logout()
  router.push("/login")
  // location.reload()
}

// 去除空字符串
export const removeEmptyFromObj = (obj: any) => {
  const newObj: any = {}
  for (const key in obj) {
    const item = obj[key]
    if (item === "" || item === null || item === undefined || item.length === 0) continue
    newObj[key] = item
  }
  return newObj
}

/** 创建请求实例 */
function createService() {
  // 创建一个 axios 实例命名为 service
  const service = axios.create()
  // 请求拦截
  service.interceptors.request.use(
    (config: any) => {
      if (config.data && !(Object.prototype.toString.call(config.data) === "[object FormData]")) {
        config.data = removeEmptyFromObj(config.data)
      }
      if (config.params) {
        config.params = removeEmptyFromObj(config.params)
      }

      if (getToken()) {
        config.headers["Authorization"] = getToken()
      }
      return config
    }
    // 发送失败
    // (error) => Promise.reject(error)
  )
  // 响应拦截（可根据具体业务作出相应的调整）
  service.interceptors.response.use(
    (response) => {
      // console.log(response, "response")
      // apiData 是 api 返回的数据
      const apiData = response.data

      // 判断apiData的message
      if (apiData.message == "未登录") {
        logout()
      }

      // 二进制数据则直接返回
      const responseType = response.request?.responseType
      if (responseType === "blob" || responseType === "arraybuffer") return apiData
      // 这个 code 是和后端约定的业务 code
      const code = apiData.error_code
      // 如果没有 code, 是后端返回的数据流
      if (code === undefined) {
        return apiData
      }
      switch (code) {
        case 0:
          // 本系统采用 code === 0 来表示没有业务错误
          return apiData
        case 1:
          return apiData
        case 2:
          return apiData
        case 200:
          return apiData
        case 201:
          // code === 201 密码还有7天到期
          return apiData
        case 202:
          // code === 202 密码到期
          if (apiData?.data?.token) {
            // 如果是登录页就去单独的修改密码页面，如果不是就清除token返回登录页
            useUserStoreHook().logout()
            router.push("/repassword")
            return apiData
          } else {
            return logout()
          }
        case 203:
          // code === 203 授权到期
          useUserStoreHook().logout()
          return router.push("/empower")
        case 204:
          // code === 204 未授权
          return apiData
        case 401:
          // Token 过期时
          return logout()
        case 403:
          // 登录超时 过期时
          useUserStoreHook().set403ErrorMsg("会话超时，请重新登录")
          return logout()
        case 405:
          // 登录人数超限
          useUserStoreHook().set403ErrorMsg("已强制退出，当前账号只允许一人登录，可联系管理员处理")
          // ElMessage.error(apiData.merge || "Error")
          return logout()
        case 1003:
          ElMessage.error("授权过期，请重新授权")
          return logout()
        // case 1001:
        // return logout()
        default:
          // 不是正确的 code
          ElMessage.error(apiData.message || "Error")
          return Promise.reject(new Error("Error"))
      }
    },
    (error) => {
      // console.log(error.response);
      // const responseError = error.console.log(error);
      // status 是 HTTP 状态码
      const status = get(error, "response.status")
      switch (status) {
        case 400:
          error.message = "请求错误"
          break
        case 401:
          // Token 过期时
          logout()
          break
        case 403:
          error.message = "拒绝访问"
          break
        case 404:
          error.message = "请求地址出错"
          break
        case 405:
          error.message = "请求方法错误"
          break
        case 408:
          error.message = "请求超时"
          break
        case 500:
          error.message = "服务器内部错误"
          break
        case 501:
          error.message = "服务未实现"
          break
        case 502:
          error.message = "网关错误"
          break
        case 503:
          error.message = "服务不可用"
          break
        case 504:
          error.message = "网关超时"
          break
        case 505:
          error.message = "HTTP 版本不受支持"
          break
        default:
          break
      }
      ElMessage.error(error.message)
      return Promise.reject(error)
    }
  )
  return service
}

/** 创建请求方法 */
function createRequest(service: AxiosInstance) {
  return function <T>(config: AxiosRequestConfig): Promise<T> {
    const token = getToken()
    const defaultConfig = {
      headers: {
        // 携带 Token
        Authorization: token ? token : undefined
      },
      timeout: 200000,
      baseURL: import.meta.env.VITE_BASE_API
    }
    // 将默认配置 defaultConfig 和传入的自定义配置 config 进行合并成为 mergeConfig

    const mergeConfig = merge(defaultConfig, config)
    return service(mergeConfig)
  }
}

/** 用于网络请求的实例 */
const service = createService()
/** 用于网络请求的方法 */
export const request = createRequest(service)

//数据统一处理（来自网络或者服务器的错误）
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function checkStatus(response) {
  if (response.status === 401 || response.data?.code === 401) {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    if ((window as any).reloadStart) {
      return {
        msg: "请重新登录!!",
        code: 401,
        res: response.data?.res || null,
        traceId: response.data?.traceId || null,
        exception: response.data?.exception || null
      }
    }
    ;(window as any).reloadStart = true

    return {
      msg: "请重新登录!!",
      code: 401,
      res: response.data?.res || null,
      traceId: response.data?.traceId || null,
      exception: response.data?.exception || null
    }
  } else if (response.status === 424 || response.data?.code === 424) {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    if ((window as any).reloadStart) {
      return {
        msg: "系统检测到您的帐号已在另一台设备上登录，请确认账号安全，重新登录!!",
        code: 401,
        res: response.data?.res || null,
        traceId: response.data?.traceId || null,
        exception: response.data?.exception || null
      }
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    ;(window as any).reloadStart = true

    return {
      msg: "系统检测到您的帐号已在另一台设备上登录，请确认账号安全，重新登录!!",
      code: 401,
      res: response.data?.res || null,
      traceId: response.data?.traceId || null,
      exception: response.data?.exception || null
    }
  } else {
    return response.data
      ? response.data
      : {
          msg: "网络请求异常!!",
          code: 500,
          res: null,
          traceId: null,
          exception: null
        }
  }
}

/**
 * 封装网路请求方式,与抛出请求地址
 */
export default class HttpUtil {
  /**
   * put 请求 后端使用@RequestBody时使用
   * @param url 请求地址
   * @param data 请求参数
   */
  static async putJson(
    url: string,
    data?: object
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
  ) {
    if (data) {
      data = removeEmptyFromObj(data) || ""
    }
    const response = await axios({
      method: "put",
      url: url,
      data: JSON.stringify(data),
      headers: { "Content-Type": "application/json;charset=utf-8" } //设置header信息
    })
    return checkStatus(response)
  }

  /**
   * get 请求
   * @param url 请求地址
   * @param params 请求参数
   */
  static async getPdf(url: string, params?: object) {
    const response = await axios({
      method: "get",
      url: url,
      params,
      headers: {
        "Content-Type": "application/pdf"
      }
    })
    return checkStatus(response)
  }

  /**
   * 文件流下载文件
   * @param url 请求地址
   * @param data 请求参数
   */
  static async download(url: string, fileName: string, params?: object, callback?: Function) {
    if (params) {
      params = removeEmptyFromObj(params) || ""
    }
    const response = await axios({
      method: "get",
      url,
      responseType: "blob",
      headers: { Authorization: getToken() },
      params,
      onDownloadProgress: function (progressEvent) {
        if (callback) {
          callback(progressEvent)
        }
      }
    })
    if ([401, 500].includes(checkStatus(response).code || 0)) return checkStatus(response)
    const file = checkStatus(response) as unknown as Blob
    const fileUrl = URL.createObjectURL(new Blob([file || new Blob()]))
    const link = document.createElement("a")
    link.style.display = "none"
    link.href = fileUrl
    link.setAttribute("download", fileName)
    document.body.appendChild(link)
    link.click()
    // loading(false)
    return {
      code: 200,
      msg: "文件下载成功",
      res: true,
      traceId: null,
      exception: null,
      response
    }
  }

  static async downloadExcel(url: string, fileName: string, data?: object, callback?: Function) {
    if (data) {
      data = data || ""
    }
    const response = await axios({
      method: "post",
      url,
      responseType: "blob",
      headers: { Authorization: getToken() },
      data,
      onDownloadProgress: function (progressEvent) {
        if (callback) {
          callback(progressEvent)
        }
      }
    })
    if ([401, 500].includes(checkStatus(response).code || 0)) return checkStatus(response)
    const file = checkStatus(response) as unknown as Blob
    const fileUrl = URL.createObjectURL(new Blob([file || new Blob()]))
    const link = document.createElement("a")
    link.style.display = "none"
    link.href = fileUrl
    link.setAttribute("download", fileName)
    document.body.appendChild(link)
    link.click()
    return {
      code: 200,
      msg: "文件下载成功",
      res: true,
      traceId: null,
      exception: null
    }
  }

  /**
   * 文件流上传文件
   * @param url 请求地址
   * @param data 请求参数
   */
  static async upload(
    url: string,
    fileName: string,
    data?: Blob
    // eslint-disable-next-line
  ) {
    if (data) {
      data = data || ""
    }
    const formData = new FormData()
    formData.append(fileName, data || "")
    const response = await axios({
      method: "post",
      url,
      data: formData,
      headers: {
        "Content-Type": "multipart/form-data", // 请求头 类型
        Authorization: localStorage.getItem("token")
      }
    })
    return checkStatus(response)
  }

  static getTokenHeader() {
    return null
  }

  static getTokenParam() {
    return ""
  }
}
